

#include <stdbool.h>
#include <stdint.h>

#include <platform.h>

#if defined(USE_PWM) || defined(USE_PPM)

#include "build/build_config.h"
#include "build/debug.h"

#include "common/utils.h"

#include "drivers/exti.h"
#include "drivers/nvic.h"
#include "drivers/io.h"
#include "drivers/time.h"

#include "pwm_output.h"
#include "rx_pwm.h"

#include "flight/mixer.h" //!!TODO remove dependency on this

#define DEBUG_PPM_ISR

#define PPM_CAPTURE_COUNT 12

#if PPM_CAPTURE_COUNT > PWM_INPUT_PORT_COUNT
#define PWM_PORTS_OR_PPM_CAPTURE_COUNT PPM_CAPTURE_COUNT
#else
#define PWM_PORTS_OR_PPM_CAPTURE_COUNT PWM_INPUT_PORT_COUNT
#endif

// TODO - change to timer clocks ticks
#define INPUT_FILTER_TO_HELP_WITH_NOISE_FROM_OPENLRS_TELEMETRY_RX 0x03

static inputFilteringMode_e inputFilteringMode;

void pwmICConfig(TIM_TypeDef *tim, uint8_t channel, uint16_t polarity);

typedef enum {
    INPUT_MODE_PPM,
    INPUT_MODE_PWM
} pwmInputMode_e;

typedef struct {
    pwmInputMode_e mode;
    uint8_t channel; // only used for pwm, ignored by ppm

    uint8_t state;
    uint8_t missedEvents;

    captureCompare_t rise;
    captureCompare_t fall;
    captureCompare_t capture;

    const timerHardware_t *timerHardwareField;
    timerCCHandlerRec_t edgeCb;
    timerOvrHandlerRec_t overflowCb;
} pwmInputPort_t;

static pwmInputPort_t pwmInputPorts[PWM_INPUT_PORT_COUNT];

static uint16_t captures[PWM_PORTS_OR_PPM_CAPTURE_COUNT];

#define PPM_TIMER_PERIOD 0x10000
#define PWM_TIMER_PERIOD 0x10000

static uint8_t ppmFrameCount = 0;
static uint8_t lastPPMFrameCount = 0;
static uint8_t ppmCountDivisor = 1;

typedef struct ppmDevice_s {
    //uint32_t previousTime;
    uint32_t currentCapture;
    uint32_t currentTime;
    uint32_t deltaTime;
    uint32_t captures[PWM_PORTS_OR_PPM_CAPTURE_COUNT];
    uint32_t largeCounter;
    uint8_t  pulseIndex;
    int8_t   numChannels;
    int8_t   numChannelsPrevFrame;
    uint8_t  stableFramesSeenCount;

    bool     tracking;
    bool     overflowed;
} ppmDevice_t;

#define PPM_IN_MIN_SYNC_PULSE_US    2700    // microseconds
#define PPM_IN_MIN_CHANNEL_PULSE_US 750     // microseconds
#define PPM_IN_MAX_CHANNEL_PULSE_US 2250    // microseconds
#define PPM_STABLE_FRAMES_REQUIRED_COUNT    25
#define PPM_IN_MIN_NUM_CHANNELS     4
#define PPM_IN_MAX_NUM_CHANNELS     PWM_PORTS_OR_PPM_CAPTURE_COUNT

bool isPPMDataBeingReceived(void)
{
    return (ppmFrameCount != lastPPMFrameCount);
}

void resetPPMDataReceivedState(void)
{
    lastPPMFrameCount = ppmFrameCount;
}

#define MIN_CHANNELS_BEFORE_PPM_FRAME_CONSIDERED_VALID 4

#ifdef DEBUG_PPM_ISR
typedef enum {
    SOURCE_OVERFLOW = 0,
    SOURCE_EDGE = 1
} eventSource_e;

typedef struct ppmISREvent_s {
    uint32_t capture;
    eventSource_e source;
} ppmISREvent_t;

static ppmISREvent_t ppmEvents[20];
static uint8_t ppmEventIndex = 0;

void ppmISREvent(eventSource_e source, uint32_t capture)
{
    ppmEventIndex = (ppmEventIndex + 1) % (sizeof(ppmEvents) / sizeof(ppmEvents[0]));

    ppmEvents[ppmEventIndex].source = source;
    ppmEvents[ppmEventIndex].capture = capture;
}
#else
void ppmISREvent(eventSource_e source, uint32_t capture) {}
#endif

#define MAX_MISSED_PWM_EVENTS 10

bool isPWMDataBeingReceived(void)
{
    int channel;
    for (channel = 0; channel < PWM_PORTS_OR_PPM_CAPTURE_COUNT; channel++) {
        if (captures[channel] != PPM_RCVR_TIMEOUT) {
            return true;
        }
    }
    return false;
}


//#ifndef PCINT
static void pwmOverflowCallback(timerOvrHandlerRec_t* cbRec, captureCompare_t capture)
{
    UNUSED(capture);
    pwmInputPort_t *pwmInputPort = container_of(cbRec, pwmInputPort_t, overflowCb);

    if (++pwmInputPort->missedEvents > MAX_MISSED_PWM_EVENTS) {
        captures[pwmInputPort->channel] = PPM_RCVR_TIMEOUT;
        pwmInputPort->missedEvents = 0;
    }
}

static void pwmEdgeCallback(timerCCHandlerRec_t *cbRec, captureCompare_t capture)
{
    pwmInputPort_t *pwmInputPort = container_of(cbRec, pwmInputPort_t, edgeCb);
    const timerHardware_t *timerHardwarePtr = pwmInputPort->timerHardwareField;

    if (pwmInputPort->state == 0) {
        pwmInputPort->rise = capture;
        pwmInputPort->state = 1;
        pwmICConfig(timerHardwarePtr->tim, timerHardwarePtr->channel, TIM_ICPolarity_Falling);
    } else {
        pwmInputPort->fall = capture;

        // compute and store capture
        pwmInputPort->capture = pwmInputPort->fall - pwmInputPort->rise;
        captures[pwmInputPort->channel] = pwmInputPort->capture;

        // switch state
        pwmInputPort->state = 0;
        pwmICConfig(timerHardwarePtr->tim, timerHardwarePtr->channel, TIM_ICPolarity_Rising);
        pwmInputPort->missedEvents = 0;
    }
}
//#endif

void pwmICConfig(TIM_TypeDef *tim, uint8_t channel, uint16_t polarity)
{
    TIM_ICInitTypeDef TIM_ICInitStructure;

    TIM_ICStructInit(&TIM_ICInitStructure);
    TIM_ICInitStructure.TIM_Channel = channel;
    TIM_ICInitStructure.TIM_ICPolarity = polarity;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;

    if (inputFilteringMode == INPUT_FILTERING_ENABLED) {
        TIM_ICInitStructure.TIM_ICFilter = INPUT_FILTER_TO_HELP_WITH_NOISE_FROM_OPENLRS_TELEMETRY_RX;
    } else {
        TIM_ICInitStructure.TIM_ICFilter = 0x00;
    }

    TIM_ICInit(tim, &TIM_ICInitStructure);
}


// static IO_t pwmIO1;
// static IO_t pwmIO2;
// static IO_t pwmIO3;
// static IO_t pwmIO4;
// static IO_t pwmIO5;
// static IO_t pwmIO6;
// static bool lastStatus1;
// static bool lastStatus2;
// static bool lastStatus3;
// static bool lastStatus4;
// static bool lastStatus5;
// static bool lastStatus6;
// static uint32_t upTime1;
// static uint32_t upTime2;
// static uint32_t upTime3;
// static uint32_t upTime4;
// static uint32_t upTime5;
// static uint32_t upTime6;
// extiCallbackRec_t pwm1_extiCallbackRec;
// extiCallbackRec_t pwm2_extiCallbackRec;
// extiCallbackRec_t pwm3_extiCallbackRec;
// extiCallbackRec_t pwm4_extiCallbackRec;
// extiCallbackRec_t pwm5_extiCallbackRec;
// extiCallbackRec_t pwm6_extiCallbackRec;


// void pwm1_extiHandler(extiCallbackRec_t *cb)
// {
//     UNUSED(cb);
//     bool status = IORead(pwmIO1);
//     if (status != lastStatus1)
//     {
//         if (status) //上升
//         {
//             upTime1 = micros();
//         }
//         else //下降
//         {
//             captures[1 - 1] = micros() - upTime1;
//         }
//         lastStatus1 = status;
//     }
// }
// void pwm2_extiHandler(extiCallbackRec_t *cb)
// {
//     UNUSED(cb);
//     bool status = IORead(pwmIO2);
//     if (status != lastStatus2)
//     {
//         if (status) //上升
//         {
//             upTime2 = micros();
//         }
//         else //下降
//         {
//             captures[2 - 1] = micros() - upTime2;
//         }
//         lastStatus2 = status;
//     }
// }
// void pwm3_extiHandler(extiCallbackRec_t *cb)
// {
//     UNUSED(cb);
//     bool status = IORead(pwmIO3);
//     if (status != lastStatus3)
//     {
//         if (status) //上升
//         {
//             upTime3 = micros();
//         }
//         else //下降
//         {
//             captures[3 - 1] = micros() - upTime3;
//         }
//         lastStatus3 = status;
//     }
// }
// void pwm4_extiHandler(extiCallbackRec_t *cb)
// {
//     UNUSED(cb);
//     bool status = IORead(pwmIO4);
//     if (status != lastStatus4)
//     {
//         if (status) //上升
//         {
//             upTime4 = micros();
//         }
//         else //下降
//         {
//             captures[4 - 1] = micros() - upTime4;
//         }
//         lastStatus4 = status;
//     }
// }
// void pwm5_extiHandler(extiCallbackRec_t *cb)
// {
//     UNUSED(cb);
//     bool status = IORead(pwmIO5);
//     if (status != lastStatus5)
//     {
//         if (status) //上升
//         {
//             upTime5 = micros();
//         }
//         else //下降
//         {
//             captures[5 - 1] = micros() - upTime5;
//         }
//         lastStatus5 = status;
//     }
// }
// void pwm6_extiHandler(extiCallbackRec_t *cb)
// {
//     UNUSED(cb);
//     bool status = IORead(pwmIO6);
//     if (status != lastStatus6)
//     {
//         if (status) //上升
//         {
//             upTime6 = micros();
//         }
//         else //下降
//         {
//             captures[6 - 1] = micros() - upTime6;
//         }
//         lastStatus6 = status;
//     }
// }

void pwmRxInit(const pwmConfig_t *pwmConfig)
{
    inputFilteringMode = pwmConfig->inputFilteringMode;

// #ifdef PCINT
//     pwmIO1 = IOGetByTag(pwmConfig->ioTags[0]);//TODO 换成从timerHardwarePWM tag取
//     IOConfigGPIO(pwmIO1, IOCFG_IN_FLOATING);
//     pwm1_extiCallbackRec.fn = pwm1_extiHandler;
//     EXTIConfig(pwmIO1, &pwm1_extiCallbackRec, NVIC_BUILD_PRIORITY(2, 0), EXTI_Trigger_Rising_Falling); // TODO - priority!
//     EXTIEnable(pwmIO1, true);

//     pwmIO2 = IOGetByTag(pwmConfig->ioTags[1]);
//     IOConfigGPIO(pwmIO2, IOCFG_IN_FLOATING);
//     pwm2_extiCallbackRec.fn = pwm2_extiHandler;
//     EXTIConfig(pwmIO2, &pwm2_extiCallbackRec, NVIC_BUILD_PRIORITY(2, 0), EXTI_Trigger_Rising_Falling); // TODO - priority!
//     EXTIEnable(pwmIO2, true);

//     pwmIO3 = IOGetByTag(pwmConfig->ioTags[2]);
//     IOConfigGPIO(pwmIO3, IOCFG_IN_FLOATING);
//     pwm3_extiCallbackRec.fn = pwm3_extiHandler;
//     EXTIConfig(pwmIO3, &pwm3_extiCallbackRec, NVIC_BUILD_PRIORITY(2, 0), EXTI_Trigger_Rising_Falling); // TODO - priority!
//     EXTIEnable(pwmIO3, true);

//     pwmIO4 = IOGetByTag(pwmConfig->ioTags[3]);
//     IOConfigGPIO(pwmIO4, IOCFG_IN_FLOATING);
//     pwm4_extiCallbackRec.fn = pwm4_extiHandler;
//     EXTIConfig(pwmIO4, &pwm4_extiCallbackRec, NVIC_BUILD_PRIORITY(2, 0), EXTI_Trigger_Rising_Falling); // TODO - priority!
//     EXTIEnable(pwmIO4, true);

//     pwmIO5 = IOGetByTag(pwmConfig->ioTags[4]);
//     IOConfigGPIO(pwmIO5, IOCFG_IN_FLOATING);
//     pwm5_extiCallbackRec.fn = pwm5_extiHandler;
//     EXTIConfig(pwmIO5, &pwm5_extiCallbackRec, NVIC_BUILD_PRIORITY(2, 0), EXTI_Trigger_Rising_Falling); // TODO - priority!
//     EXTIEnable(pwmIO5, true);

//     pwmIO6 = IOGetByTag(pwmConfig->ioTags[5]);
//     IOConfigGPIO(pwmIO6, IOCFG_IN_FLOATING);
//     pwm6_extiCallbackRec.fn = pwm6_extiHandler;
//     EXTIConfig(pwmIO6, &pwm6_extiCallbackRec, NVIC_BUILD_PRIORITY(2, 0), EXTI_Trigger_Rising_Falling); // TODO - priority!
//     EXTIEnable(pwmIO6, true);
// #else

    for (int channel = 0; channel < PWM_INPUT_PORT_COUNT; channel++) {

        pwmInputPort_t *port = &pwmInputPorts[channel];

        const timerHardware_t *timer = &timerHardwarePWM[channel];//timerGetByTag(pwmConfig->ioTags[channel], TIM_USE_ANY);

        if (!timer) {
            /* TODO: maybe fail here if not enough channels? */
            continue;
        }

        port->state = 0;
        port->missedEvents = 0;
        port->channel = channel;
        port->mode = INPUT_MODE_PWM;
        port->timerHardwareField = timer;

        IO_t io = IOGetByTag(timer->tag);//pwmConfig->ioTags[channel]
        IOInit(io, OWNER_PWMINPUT, RESOURCE_INDEX(channel));

        IOConfigGPIO(io, IOCFG_IPD);

        timerConfigure(timer, (uint16_t)PWM_TIMER_PERIOD, PWM_TIMER_1MHZ);
        timerChCCHandlerInit(&port->edgeCb, pwmEdgeCallback);
        timerChOvrHandlerInit(&port->overflowCb, pwmOverflowCallback);
        timerChConfigCallbacks(timer, &port->edgeCb, &port->overflowCb);

        pwmICConfig(timer->tim, timer->channel, TIM_ICPolarity_Rising);


    }
    
//#endif
}

#define UNUSED_PPM_TIMER_REFERENCE 0
#define FIRST_PWM_PORT 0

void ppmAvoidPWMTimerClash(TIM_TypeDef *pwmTimer)
{
    pwmOutputPort_t *motors = pwmGetMotors();
    for (int motorIndex = 0; motorIndex < MAX_SUPPORTED_MOTORS; motorIndex++) {
        if (!motors[motorIndex].enabled || motors[motorIndex].channel.tim != pwmTimer) {
            continue;
        }

        ppmCountDivisor = timerClock(pwmTimer) / (pwmTimer->PSC + 1);
        return;
    }
}

uint16_t ppmRead(uint8_t channel)
{
    return captures[channel];
}

uint16_t pwmRead(uint8_t channel)
{
    //debuginfoSlowInt[4+channel] = captures[channel];
    return captures[channel];
}
#endif
